/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.generator;

/**
 *
 * @author Laurent WOUTERS
 */
public class PropertyImplementation extends PropertyRepresentation {
    protected java.util.List<PropertyInterface> interfaces;
    protected java.util.List<PropertyInterface> masked;
    protected ClassGenerator propertyDomain;
    protected ClassGenerator propertyRange;
    protected java.util.List<PropertyImplementation> implAncestors;
    protected java.util.List<PropertyImplementation> implDescendants;
    protected java.util.List<PropertyGenerator> implInverses;
    
    public java.util.Collection<PropertyInterface> getInterfaces() { return interfaces; }
    public ClassGenerator getDomain() { return propertyDomain; }
    public ClassGenerator getRange() { return propertyRange; }
    public java.util.List<PropertyImplementation> getAncestors() { return implAncestors; }
    public java.util.List<PropertyImplementation> getDescendants() { return implDescendants; }
    public java.util.List<PropertyGenerator> getInverses() { return implInverses; }
    
    public PropertyImplementation(ClassGenerator classe, PropertyGenerator property) {
        super(classe, property);
        propertyDomain = property.getDomain();
        if (property.isObjectProperty())
            propertyRange = property.getRangeClass();
        interfaces = new java.util.ArrayList<PropertyInterface>();
        masked = new java.util.ArrayList<PropertyInterface>();
        implAncestors = new java.util.ArrayList<PropertyImplementation>();
        implDescendants = new java.util.ArrayList<PropertyImplementation>();
        implInverses = new java.util.ArrayList<PropertyGenerator>();
    }


    public void addInterface(PropertyInterface inter) {
        for (PropertyInterface current : interfaces) {
            if (current.sameAs(inter)) {
                masked.add(inter);
                return;
            }
        }
        interfaces.add(inter);
    }

    public void close() {
        inTypeRestrictionChain = false;
        for (PropertyInterface inter : interfaces) {
            if (inter.restrictType()) {
                inTypeRestrictionChain = true;
                break;
            }
        }
        if (!inTypeRestrictionChain) {
            for (PropertyInterface inter : masked) {
                if (inter.restrictType()) {
                    inTypeRestrictionChain = true;
                    break;
                }
            }
        }
        
        if (inTypeRestrictionChain) {
            for (PropertyInterface inter : interfaces)
                inter.setInTypeRestrictionChain();
            for (PropertyInterface inter : masked)
                inter.setInTypeRestrictionChain();
        }

        if (property.isObjectProperty()) {
            String header = "Property " + property.getName() + " : " + parentClass.getJavaName() + " -> " + rangeClass.getJavaName();
            java.util.List<String> errors = new java.util.ArrayList<String>();
            if (property.isSymmetric()) {
                if (!property.hasInverse())
                    errors.add("Property is symmetric but has no inverse");
                else if (property.getInverse() != property)
                    errors.add("Property is symmetric but the inverse property is different");
            }
            if (property.isReflexive()) {
                if (!parentClass.isCompatibleWith(rangeClass))
                    errors.add("Property is reflexive but the domain is not a subset of the range");
            }
            if (property.hasInverse()) {
                ClassGenerator inverseDomain = property.getInverse().getDomain();
                ClassGenerator inverseRange = property.getInverse().getRangeClass();
                if (propertyDomain != inverseRange)
                    errors.add("Property domain is different from the range of the inverse property " + property.getInverse().getName());
                if (propertyRange != inverseDomain)
                    errors.add("Property range is different from the domain of the inverse property " + property.getInverse().getName());
            }
            if (!errors.isEmpty()) {
                java.lang.System.out.println(header);
                for (String error : errors)
                    java.lang.System.out.println("    " + error);
            }
        }
    }

    public void buildPropertyGraph() {
        if (property.isObjectProperty()) {
            for (PropertyGenerator parent : property.getSuperProperties())
                implAncestors.add(parentClass.getPropertyImplementation(parent));
            for (int i=0; i!=implAncestors.size(); i++) {
                for (PropertyGenerator parent : implAncestors.get(i).getProperty().getDirectSuperProperties()) {
                    PropertyImplementation impl = parentClass.getPropertyImplementation(parent);
                    if (!implAncestors.contains(impl))
                        implAncestors.add(impl);
                }
            }
            if (property.hasInverse())
                implInverses.add(property.getInverse());
            for (PropertyImplementation ancestor : implAncestors)
                if (ancestor.getProperty().hasInverse())
                    implInverses.add(ancestor.getProperty().getInverse());
            buildPropertyGraph_GetDescendants(this);
        }
    }
    private void buildPropertyGraph_GetDescendants(PropertyImplementation property) {
        if (property.property.getSubProperties().isEmpty()) {
            if (property != this)
                implDescendants.add(property);
            return;
        }
        for (PropertyGenerator child : property.property.getDirectSubProperties()) {
            ClassGenerator childDomain = child.getDomain();
            if (parentClass.isCompatibleWith(childDomain)) {
                PropertyImplementation childImpl = parentClass.getPropertyImplementation(child);
                buildPropertyGraph_GetDescendants(childImpl);
            }
        }
        if (property != this)
            implDescendants.add(property);
    }
}
